package com.alisls.demo.springcloud.service.user.config;

import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.cloud.context.config.annotation.RefreshScope;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.http.SessionCreationPolicy;
import org.springframework.security.oauth2.config.annotation.web.configuration.EnableResourceServer;
import org.springframework.security.oauth2.config.annotation.web.configuration.ResourceServerConfigurerAdapter;
import org.springframework.security.oauth2.config.annotation.web.configurers.ResourceServerSecurityConfigurer;
import org.springframework.security.oauth2.provider.token.TokenStore;

import javax.annotation.Resource;
import java.util.List;

/**
 * 资源服务器配置类
 * 注解 @EnableResourceServer 开启资源服务器（获取资源服务器中的资源，必须带有token才能访问）
 * 注解 @EnableGlobalMethodSecurity(prePostEnabled = true) 开启方法级别权限控制
 *
 * @author Ke Wang
 */
@Configuration
@RefreshScope
@EnableResourceServer
@EnableGlobalMethodSecurity(prePostEnabled = true)
@Slf4j
public class ResourceServerConfig extends ResourceServerConfigurerAdapter {

    /**
     * SpringSecurity资源服务器的资源ID属性
     */
    @Value("${service.user.resource-id}")
    private String resourceId;

    /**
     * Spring Security Scope属性
     */
    @Value("${service.user.scope}")
    private String scope;

    /**
     * 匿名访问的url
     */
    private List<String> ignoreUrls;

    /**
     * 属性配置类
     */
    @Resource
    private ServiceUserProp serviceUserProp;

    /**
     * Token存储管理
     */
    @Autowired
    private TokenStore tokenStore;

    /**
     * 资源服务器配置类
     * @param resources
     * @throws Exception
     */
    @Override
    public void configure(ResourceServerSecurityConfigurer resources) throws Exception {

        // 设置当前资源服务器的ID，认证服务会认证客户端有没有访问这个资源ID的权限
        resources.resourceId(resourceId)
                // 设置tokenStore, 使用JWT token进行校验
                .tokenStore(tokenStore);
    }

    /**
     * 控制令牌范围权限和授权规则
     * @param http
     * @throws Exception
     */
    @Override
    public void configure(HttpSecurity http) throws Exception {
        // 设定Session策略为不创建HttpSession实例
        http.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS);

        // 设置可以匿名访问的资源
        setIgnoreUrls(http);

        // 设置资源访问授权规则
        http.authorizeRequests()
                // 设置访问资源/user/listUser需要权限user:listUser（注意顺序，不能放到后面来写）
                .antMatchers("/user/listUser").hasAuthority("user:listUser")
                // 设定资源服务器要求所有请求都必须有all范围(在认证服务器上针对客户端配置的范围)
                .antMatchers("/**").access("#oauth2.hasScope('" + scope + "')");
    }

    /**
     * 设置可以匿名访问的url
     * @param http
     * @throws Exception
     */
    private void setIgnoreUrls(HttpSecurity http) throws Exception {
        ignoreUrls = serviceUserProp.getIgnoreUrls();
        if (ignoreUrls == null) {
            return;
        }

        log.info("用户中心可以匿名访问的url列表：");
        for (String url: ignoreUrls) {
            log.info(url);
            http.authorizeRequests().antMatchers(url).anonymous();
        }
    }

}

